package hr.fer.apr.ht41337.lab3;

import hr.fer.apr.ht41337.lab2.funkcije.Funkcija;

import java.io.*;
import java.util.Random;

public class GA {
	
	final private int dg = -50;
	final private int gg = 100;
	final private double preciznost = 1e-3;
	private static Random rnd = new Random();
	
	private Funkcija f;
	private int velicinaPopulacije;
	private double pMutacije;
	private int brIteracija;
	
	private int n;
	private Jedinka[] jedinka;
	private int[] odabrane = new int[3];
	
	BufferedWriter bw;
	
	public GA(Funkcija f, int velicinaPopulacije, double pMutacije, int brIteracija) {
		
		this.velicinaPopulacije = velicinaPopulacije;
		this.pMutacije = pMutacije;
		this.brIteracija = brIteracija;
		this.f = f;
		
		//broj bitova u binarnom prikazu rjesjenja
		n = (int)Math.ceil(Math.log( (gg-dg) / preciznost - 1) / Math.log(2));
		jedinka = new Jedinka[velicinaPopulacije];
		
		 try {
			bw = new BufferedWriter(new FileWriter("izlaz.txt"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public double minimiziraj() throws IOException
	{
		//index najlosije jedinke koju zamjenjuje nova jedinka
		int nova;
		Jedinka novaJedinka;
		
		stvoriPopulaciju();
		int najbolja = evaluirajPopulaciju();
		
		for (int i = 0; i < brIteracija; i++)
		{
			odaberiJedinke();
			nova = nadjiNajlosiju();
			novaJedinka = krizajPreostaleDvije(nova);
//			bw.write(""+novaJedinka.getTocka());
			novaJedinka.mutiraj(pMutacije);
			jedinka[nova] = novaJedinka;
//			bw.write(f.getValue(jedinka[najbolja].getTocka())+"");
//			bw.newLine();
			if (f.getValue(novaJedinka.getTocka()) < f.getValue(jedinka[najbolja].getTocka()))
				najbolja = nova;
			
		}
		System.out.println(jedinka[najbolja].getTocka());
		for (int i = 0; i < jedinka[0].getTocka().dimenzija; i++)
			System.out.print(jedinka[najbolja].getB(i) + " ");
		System.out.println();
		for (int i = 0; i < jedinka[0].getTocka().dimenzija; i++)
			System.out.print(Integer.toBinaryString(jedinka[najbolja].getB(i)) + " ");
		System.out.println();
		return f.getValue(jedinka[najbolja].getTocka());
	}
	
	private void stvoriPopulaciju()
	{
		for (int i = 0; i < velicinaPopulacije; i++)
			jedinka[i] = new Jedinka(f, n, dg, gg);
	}
	
	private int evaluirajPopulaciju()
	{
		int min = 0;
		for (int i = 1; i < velicinaPopulacije; i++)
			if (f.getValue(jedinka[i].getTocka()) < f.getValue(jedinka[min].getTocka()))
				min = i;
		return min;
	}
	
	private void odaberiJedinke()
	{
		int prva = 0;
		int druga = 0;
		int treca = 0;
		while (prva == druga || prva == treca || druga == treca)
		{
			prva = rnd.nextInt(velicinaPopulacije);
			druga = rnd.nextInt(velicinaPopulacije);
			treca = rnd.nextInt(velicinaPopulacije);
		}
		odabrane[0] = prva;
		odabrane[1] = druga;
		odabrane[2] = treca;
		//System.out.print(odabrane[0] + " " + odabrane[1] + " " + odabrane[2] + " ");
	}
	
	private int nadjiNajlosiju()
	{
		int najlosija = odabrane[0];
		for (int i = 1; i < 3; i++)
			if (f.getValue(jedinka[odabrane[i]].getTocka()) > f.getValue(jedinka[najlosija].getTocka()))
				najlosija = odabrane[i];
		//System.out.println(najlosija);
		return najlosija;
	}
	
	private Jedinka krizajPreostaleDvije(int najlosija)
	{
		Jedinka prva;
		Jedinka druga;
		if (najlosija != odabrane[0])
		{
			prva = jedinka[odabrane[0]];
			if (najlosija != odabrane[1])
				druga = jedinka[odabrane[1]];
			else 
				druga = jedinka[odabrane[2]];
		}
		else
		{
			prva = jedinka[odabrane[1]];
			druga = jedinka[odabrane[2]];
		}
		return prva.krizaj(druga);
	}
	
}
